package org.raymond.iworks.study.netty.official.fundamental.discard;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import org.raymond.iworks.study.netty.official.util.ServerUtil;

/**
 * @author raymond
 * @version V1.0
 * @Description: Discards any incoming data.
 */
public class DiscardServer {
    private int port;

    public DiscardServer(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        // Configure SSL.
        final SslContext sslCtx = ServerUtil.buildSslContext();

        // NioEventLoopGroup是一个多线程的I/O操作事件循环池,Netty为各种传输方式提供了多种EventLoopGroup的实现.
        // 我们可以像这个例子一样来实现一个服务器应用,代码中的两个NioEventLoopGroup都会被使用到.
        // 第一个NioEventLoopGroup通常被称为'boss',用于接收所有连接到服务器端的客户端连接.
        // 第二个被称为'worker',当有新的连接进来时将会被注册到worker中.
        // 至于要在EventLoopGroup创建多少个线程,映射多少个Channel可以在EventLoopGroup的构造方法中进行配置
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)

                    .channel(NioServerSocketChannel.class)
                    // 我们总是为新连接到服务器的handler分配一个新的Channel.
                    .handler(new LoggingHandler(LogLevel.INFO))
                    // ChannelInitializer用于配置新生成的Channel,就和你通过配置ChannelPipeline来配置Channel是一样的效果.
                    // 考虑到应用程序的复杂性,你可以采用一个匿名类来向pipeline中添加更多的handler
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        // The handler specified here will always be evaluated by a newly accepted Channel.
                        @Override
                        public void initChannel(SocketChannel ch) {
                            ChannelPipeline p = ch.pipeline();
                            if (sslCtx != null) {
                                p.addLast(sslCtx.newHandler(ch.alloc()));
                            }
                            p.addLast(new DiscardServerHandler());
                        }
                    })
                    // option()方法用于设置监听套接字。
                    .option(ChannelOption.SO_BACKLOG, 128)
                    // childOption()则用于设置连接到服务器的客户端套接字
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
            ;

            // Bind and start to accept incoming connections.
            ChannelFuture f = b.bind(port).sync();

            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 12000;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        }
        new DiscardServer(port).run();
    }
}